package jmetal.metaheuristics.singleObjective.island;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.RecursiveTask;

import jmetal.core.Operator;
import jmetal.core.Solution;
import jmetal.core.SolutionSet;
import jmetal.util.JMException;

public class IslandFTGA extends RecursiveTask<SolutionSet> {
	private int populationSize;
	private int maxEvaluations;
	private SolutionSet population;
	private Operator mutationOperator;
	private Operator crossoverOperator;
	private Operator selectionOperator;
	private IslandFTGAController controller;
	private Comparator comparator;

	// protected static int sThreshold = 98;

	public IslandFTGA(int populationSize, int maxEvaluations, SolutionSet population, Operator mutationOperator,
			Operator crossoverOperator, Operator selectionOperator, IslandFTGAController controller,
			Comparator comparator) {
		super();
		this.populationSize = populationSize;
		this.maxEvaluations = maxEvaluations;
		this.population = population;
		this.mutationOperator = mutationOperator;
		this.crossoverOperator = crossoverOperator;
		this.selectionOperator = selectionOperator;
		this.controller = controller;
		this.comparator = comparator;

	}

	protected SolutionSet compute() {
		SolutionSet result = new SolutionSet(populationSize);

		if (maxEvaluations == 0) {
			return null;
		}

		if (maxEvaluations == 1) {
			generateOffsprings(result);
			return result;
		}

		List<RecursiveTask<SolutionSet>> forks = new ArrayList<>();

		for (int i = 0; i < maxEvaluations/populationSize; i++) {
			IslandFTGA unit = new IslandFTGA(populationSize, 1, population, mutationOperator, crossoverOperator,
					selectionOperator, controller, comparator);
			unit.fork();
			forks.add(unit);
		}

		// Somando o resultado das tarefas
		result.add(population.get(0));
		result.add(population.get(1));
		for (RecursiveTask<SolutionSet> subTask : forks) {
			Solution best = result.get(0);
			Solution best1 = result.get(1);
			result.clear();
			result.add(best);
			result.add(best1);
			// Pega o retorno de cada task, quando usamos join, e soma na
			// variavel RESULT
			SolutionSet setFilho = subTask.join();
			for (int i = 0; i < setFilho.size() - 2; i++) {
				result.add(setFilho.get(i));
			}
			result.sort(comparator);
		}

		return result;
	}

	private void generateOffsprings(SolutionSet result) {
		try {
			for (int i = 0; i < populationSize / 2; i++) {
				// Selection
				Solution[] parents = new Solution[2];

				parents[0] = (Solution) selectionOperator.execute(population);
				parents[1] = (Solution) selectionOperator.execute(population);

				// Crossover
				Solution[] offspring = (Solution[]) crossoverOperator.execute(parents);

				// Mutation
				mutationOperator.execute(offspring[0]);
				mutationOperator.execute(offspring[1]);

				// Evaluation of the new individual
				controller.getProblem().evaluate(offspring[0]);
				controller.getProblem().evaluate(offspring[1]);

				result.add(offspring[0]);
				result.add(offspring[1]);
				IslandFTGAController.evaluationsTotal+=2;
			}

		} catch (JMException e) {
			e.printStackTrace();
		}
	}
}
